package com.mh.system.service.impl;

import com.mh.common.core.domain.TreeSelect;
import com.mh.common.core.exception.CustomException;
import com.mh.common.utils.SecurityUtils;
import com.mh.common.utils.UserConstants;
import com.mh.system.domain.SysDept;
import com.mh.system.mapper.SysDeptMapper;
import com.mh.system.service.SysDeptService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class SysDeptServiceImpl implements SysDeptService {

    @Autowired
    private SysDeptMapper deptMapper;

    //查询部门全部数据
    @Override
    public List<SysDept> selectAllDept() {
        return deptMapper.selectAllDept();
    }

    //查询部门管理数据
    @Override
    public List<SysDept> selectDeptList(SysDept dept) {
        return deptMapper.selectDeptList(dept);
    }

    //根据部门Id查询信息
    @Override
    public SysDept selectDeptById(Long deptId) {
        return deptMapper.selectDeptById(deptId);
    }


    //新增部门信息
    @Override
    public int insertDept(SysDept dept) {
        //1. 获取当前节点的父节点
        SysDept parentDept = deptMapper.selectDeptById(dept.getParentId());

        //2. 父节点是否正常状态,则不允许新增子节点
        //public static final String DEPT_NORMAL = "0"-->0正常,1不正常
        if(!UserConstants.DEPT_NORMAL.equals(dept.getStatus())){
            throw new CustomException(500,"部门停用,不允许新增");
        }

        //3. 设置 ancestors 祖级列表字段,记录的是当前节点的所有父节点id
        //ancestors: 0,100 ; parent_id: 101 --> 拼接 0,100,101
        dept.setAncestors(parentDept.getAncestors() + "," + dept.getParentId());
        //设置创建者
        dept.setCreateBy(SecurityUtils.getUsername());

        return deptMapper.insertDept(dept);
    }

    //修改上级部门(包括当前部门和当前部门的子部门)
    @Override
    @Transactional //修改当前部门的上级 和 修改当前部门子级的祖籍列表 成为一个事务, 要么都成功,要么都失败
    public int updateDept(SysDept dept) {

        //获取现在哪个部门deptId要修改数据 --> 获取到该部门现在数据库中的数据(研发那条数据)
        SysDept rowDept = deptMapper.selectDeptById(dept.getDeptId());

        //获取传入的dept中父id --> 查询要修改的父id对应的数据(长沙那条数据)
        SysDept newParent = deptMapper.selectDeptById(dept.getParentId());

        //如果上面查询结果都不为空, 表示数据库中有此部门和父部门
        if(rowDept != null && newParent != null){
            //父部门的祖先(ancestors 0,100)+父部门的部门id(deptId 102) --> 当前传入的部门研发的祖先(ancestors)
            String newAncestors = newParent.getAncestors() + "," + dept.getDeptId();
            dept.setAncestors(newAncestors);

            //获取研发的原来祖先(ancestors)
            String oldAncestors = rowDept.getAncestors();

            //调用下面方法
            updateChildren(dept.getDeptId(),newAncestors,oldAncestors);
        }
        //设置创建者
        dept.setCreateBy(SecurityUtils.getUsername());
        return deptMapper.updateDept(dept);
    }
    //子方法: 父部门的父id有变化时,它的子部门的祖先(ancestors)修改的方法
    private void updateChildren(Long deptId, String newAncestors, String oldAncestors) {
        //.selectChildrenDeptById查询返回, 祖先(ancestors)中包含该部门deptId的Dept对象
        List<SysDept> childrenDepts = deptMapper.selectChildrenDeptById(deptId);
        //如果查询结果有的话, 表示该部门有子部门, 子部门的祖先也要一起变更
        if(!CollectionUtils.isEmpty(childrenDepts)){
            for (SysDept childrenDept : childrenDepts) {
                //newAncestors 0,100,101 ; oldAncestors 0,100,102
                //假设研发部门下还有子部门A, 那A原来的ancestors 0,100,101,103-->修改为0,100,102,103
                //所以不能直接set newAncestors 要把0,100,101部分替换为0,100,102-->调用String的.replace()
                childrenDept.setAncestors(
                        childrenDept.getAncestors().replace(oldAncestors,newAncestors));
            }
            deptMapper.updateDeptChildren(childrenDepts);
        }
    }

    //删除部门管理信息
    @Override
    public int deleteDeptById(Long deptId) {
        return deptMapper.deleteDeptById(deptId);
    }


    //校验部门名称是否唯一
    @Override
    public String checkDeptNameUnique(SysDept dept) {
        //where dept_name=#{deptName} and parent_id = #{parentId} limit 1
        //校验部门名是否唯一,调用mapper时需要传入name和parentId
        SysDept sysDept = deptMapper.checkDeptNameUnique(dept.getDeptName(), dept.getParentId());
        //查询结果非空-->表示已存在(不唯一)
        if (sysDept != null){
            return UserConstants.NOT_UNIQUE;
        }
        //查询结果为空-->表示不存在(唯一)
        return UserConstants.UNIQUE;
    }

    //是否存在子节点
    @Override
    public boolean hasChildByDeptId(Long deptId) {
        //select count(1) from sys_dept
        //where del_flag = '0' and parent_id = #{deptId} limit 1
        int row = deptMapper.hasChildByDeptId(deptId);
        return row > 0 ? true : false;
    }


    //查询部门是否存在用户
    @Override
    public boolean checkDeptExistUser(Long deptId) {
        //select count(1) from sys_user where dept_id = #{deptId} and del_flag = '0'
        //判断Dept表中的deptId是否跟user表中的deptId有关联,有的话不能删除该deptId
        int result = deptMapper.checkDeptExistUser(deptId);
        return result > 0;
    }

    //构建前端所需要下拉树结构(仅有id,name,child)
    @Override
    public List<TreeSelect> buildDeptTreeSelect(List<SysDept> depts) {
        //调用buildDeptTreeAll,形成完整的树形结构
        List<SysDept> deptTree = buildDeptTreeAll(depts);
        //在完整的属性结构基础上, 根据前端的要求封装到TreeSelect
        //.map(TreeSelect::new)是关键, 通过这个将完整的树形转换为前端所需的树形
        return deptTree.stream().map(TreeSelect::new).collect(Collectors.toList());
    }

    //子方法1: 构建部门全部属性的树结构
    private List<SysDept> buildDeptTreeAll(List<SysDept> depts) {

        //先获取depts的部门id, 放入List集合中
        List<Long> deptIds = new ArrayList<>();
        for (SysDept dept : depts) {
            //遍历全部的depts, 获取每个对象的部门id
            Long deptId = dept.getDeptId();
            deptIds.add(deptId);
        }

        //forEach返回void, 顶级对象&它的子级装入此List中
        List<SysDept> treeDept = new ArrayList<>();
        //筛选出最顶级的对象 (parentId为0的对象)
        depts.stream()
                //.contains: List方法 --> a.contains(b) 判断a里是否包含b
                //deptIds: 全部部门id ; deptOne.getParentId(): 单个对象的父id
                //筛选deptIds中没有的父id --> 表示那个父id就是顶级 (parenId 0不是任何部门id, 0就是顶级)
                .filter(deptOne-> ! deptIds.contains(deptOne.getParentId()))
                //遍历最顶级的对象
                .forEach(deptOne-> {
                    //每个对象都调用递归方法, 获取子节点, 传入全部的depts & 遍历的每个顶级对象
                    //递归方法时,每获取子级就会给deptOne设置child子级信息,一直循环至没有子级
                    recursionFn(depts,deptOne);
                    //每个遍历出来的顶级对象, 装入List中 --> 因上面的递归操作,每个顶级下已包含所有的子级
                    treeDept.add(deptOne);
                });
        return treeDept ;
    }
    //子方法2: 递归操作
    private void recursionFn(List<SysDept> depts, SysDept dept) {
        //在此判断dept是否为空,空就不走下面代码直接返回
        //目的: 防止下面getChildDepts()空指针异常
        if (dept == null) return;

        //获取子节点-->调用getChildDepts()
        List<SysDept> childDepts = getChildDepts(depts, dept);
        //是不是空,都要给形参dept设置children属性,不设置children就没有
        dept.setChildren(childDepts);

        //递归操作
        //获取的子节点每个都遍历
        for (SysDept childDept : childDepts) {
            //调用hasChild()判断,每个子节点是否还有子节点-->传入全部子节点 & 遍历的单个子节点对象
            if (hasChild(childDepts,childDept)){
                //调用自己,递归操作-->传入全部的depts & 遍历的单个子节点对象
                recursionFn(depts,childDept);
            }
        }
    }
    //子方法3: 获取子节点-->不依赖其他方法
    private List<SysDept> getChildDepts(List<SysDept> depts, SysDept dept) {

        return depts.stream()
                //筛选全部depts中的父id = 形参对象dept中的部门id
                //dept中的部门id又是其他任何部门的父id-->表示此dept有子部门
                .filter(deptOne->deptOne.getParentId().longValue() == dept.getDeptId().longValue())
                .collect(Collectors.toList());
    }
    //子方法4: 判断是否有子节点
    private boolean hasChild(List<SysDept> depts, SysDept dept){
        //非空(有子级)返回true; 空返回false
        return !getChildDepts(depts, dept).isEmpty();
    }
}
